package com.ruoyi.common.utils;

import lombok.extern.slf4j.Slf4j;

import javax.validation.constraints.NotNull;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
import java.util.Date;
import java.util.regex.Pattern;

/**
 * 时间跟字符串类型互相转换(LocalDateTime,LocalDate,ZonedDateTime)
 */
@Slf4j
public class DateUtil {
    public static String YMD = "yyyy-MM-dd";
    public static String YMD2 = "yyyyMMdd";
    public static String Y = "yyyy";
    public static String M = "MM";
    public static String D = "dd";
    public static String YM = "yyyy-MM";
    public static String HHMMSS = "HH:mm:ss";
    public static String hhMM = "hh:mm";
    public static String HHMM = "HH:mm";
    public static String YMDHMS = "yyyy-MM-dd HH:mm:ss";
    public static String YMDHMS2 = "yyyyMMddHHmmss";
    public static String HH = "HH";
    public static String MM = "mm";
    public static String SS = "ss";

    //多种日期格式对应的正则表达式
    private static Pattern[] dateFormatPattern = {
        Pattern.compile("\\s*\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}.\\d{1,3}\\s*"),
        Pattern.compile("\\s*\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}\\s*"),
        Pattern.compile("\\s*\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}\\s*"),
        Pattern.compile("\\s*\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}\\s*"),
        Pattern.compile("\\s*\\d{1,4}-\\d{1,2}-\\d{1,2}\\s*"),
        Pattern.compile("\\s*\\d{1,4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}\\s*"),
        Pattern.compile("\\s*\\d{1,4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}.\\d{1,3}\\s*"),
        Pattern.compile("\\s*\\d{1,4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{1,2}\\s*"),
        Pattern.compile("\\s*\\d{1,4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}\\s*"),
        Pattern.compile("\\s*\\d{1,4}/\\d{1,2}/\\d{1,2}\\s*")
    };

    //多种日期格式
    private static DateTimeFormatter[] dateFormat = {
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S"),
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"),
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH"),
        DateTimeFormatter.ofPattern("yyyy-MM-dd"),
        DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.S"),
        DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"),
        DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm"),
        DateTimeFormatter.ofPattern("yyyy/MM/dd HH"),
        DateTimeFormatter.ofPattern("yyyy/MM/dd"),
    };

    private static DateTimeFormatter parseDateFormat(String dateStr){
        for (int i = 0; i < dateFormatPattern.length; i ++) {
            if(dateFormatPattern[i].matcher(dateStr).find()) return dateFormat[i];
        }
        return null;
    }

    public static Long toEpochSecond(ZonedDateTime dateTime){
        return null == dateTime ? null : dateTime.toEpochSecond();
    }
    public static Long toEpochSecond(LocalDateTime dateTime){
        return null == dateTime ? null : dateTime.atZone(ZoneId.systemDefault()).toEpochSecond();
    }
    public static Long toEpochSecond(LocalDate date){
        return null == date ? null : date.atStartOfDay(ZoneId.systemDefault()).toEpochSecond();
    }

    public static ZonedDateTime toZonedDateTime(TemporalAccessor temporal){
        if(null == temporal) return null;

        ZoneId zoneId = temporal.query(TemporalQueries.zone());
        if(null != zoneId) return ZonedDateTime.from(temporal);
        else return LocalDateTime.from(temporal).atZone(ZoneId.systemDefault());
    }
    public static LocalDateTime toLocalDateTime(TemporalAccessor temporal){
        if(null == temporal) return null;

        LocalTime localTime = temporal.query(TemporalQueries.localTime());
        if(localTime == null) return toLocalDate(temporal).atStartOfDay();

        ZoneId zoneId = temporal.query(TemporalQueries.zone());
        if(null != zoneId) return ZonedDateTime.from(temporal).toLocalDateTime();

        return LocalDateTime.from(temporal);
    }
    public static LocalDate toLocalDate(TemporalAccessor temporal){
        if(null == temporal) return null;
        return LocalDate.from(temporal);
    }
    public static Instant toInstant(TemporalAccessor temporal){
        if(null == temporal) return null;
        return Instant.from(temporal);
    }

    public static ZonedDateTime toZonedDateTime(Long epochSecond){
        return null == epochSecond ? null : ZonedDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), ZoneId.systemDefault());
    }
    public static LocalDateTime toLocalDateTime(Long epochSecond){
        return null == epochSecond ? null : LocalDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), ZoneId.systemDefault());
    }
    public static LocalDate toLocalDate(Long epochSecond){
        return null == epochSecond ? null : LocalDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * 解析字符串为时间对象
     * @param str 时间字符串
     * @param pattern 日期时间格式（尝试使用此格式分析数据，如果格式不符，则使用默认的格式化解析器）
     * @param defaultDateFormatter 默认的格式化解析器
     * @return
     */
    public static TemporalAccessor toTemporalAccessor(String str, String pattern, @NotNull DateTimeFormatter defaultDateFormatter) {
        return stringToTemporal(str, pattern, defaultDateFormatter);
    }

    public static ZonedDateTime toZonedDateTime(String str, String pattern) {
        return toZonedDateTime(stringToTemporal(str, pattern, DateTimeFormatter.ISO_ZONED_DATE_TIME));
    }
    public static ZonedDateTime toZonedDateTime(String strYMDHMS) {
        return toZonedDateTime(stringToTemporal(strYMDHMS, DateUtil.YMDHMS, DateTimeFormatter.ISO_ZONED_DATE_TIME));
    }

    public static LocalDateTime toLocalDateTime(String strYMDHMS) {
        return toLocalDateTime(stringToTemporal(strYMDHMS, DateUtil.YMDHMS, DateTimeFormatter.ISO_LOCAL_DATE_TIME));
    }
    public static LocalDateTime toLocalDateTime(String str, String pattern) {
        return toLocalDateTime(stringToTemporal(str, pattern, DateTimeFormatter.ISO_LOCAL_DATE_TIME));
    }

    public static LocalDate toLocalDate(String strYMD) {
        return toLocalDate(stringToTemporal(strYMD, DateUtil.YMD, DateTimeFormatter.ISO_LOCAL_DATE));
    }
    public static LocalDate toLocalDate(String str, String pattern) {
        return toLocalDate(stringToTemporal(str, pattern, DateTimeFormatter.ISO_LOCAL_DATE));
    }

//    public static Instant toInstant(String strYMDHMS) {
//        return toInstant(stringToTemporal(strYMDHMS, DateUtil.YMDHMS, DateTimeFormatter.ISO_INSTANT));
//    }
//
//    public static Instant toInstant(String str, String pattern) {
//        return toInstant(stringToTemporal(str, pattern, DateTimeFormatter.ISO_INSTANT));
//    }

    public static String toString(ZonedDateTime time) {
        return temporalToString(time, YMDHMS, DateTimeFormatter.ISO_ZONED_DATE_TIME);
    }

    public static String toString(ZonedDateTime time, String pattern) {
        if(StringUtils.isBlank(pattern)) pattern = YMDHMS;
        return temporalToString(time, pattern, DateTimeFormatter.ISO_ZONED_DATE_TIME);
    }

    public static String toString(LocalDateTime time) {
        return temporalToString(time, YMDHMS, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
    }

    public static String toString(LocalDateTime time, String pattern) {
        if(StringUtils.isBlank(pattern)) pattern = YMDHMS;
        return temporalToString(time, pattern, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
    }

    public static String toString(LocalDate date) {
        return temporalToString(date, YMD, DateTimeFormatter.ISO_LOCAL_DATE);
    }
    public static String toString(LocalDate date, String pattern) {
        if(StringUtils.isBlank(pattern)) pattern = YMD;
        return temporalToString(date, pattern, DateTimeFormatter.ISO_LOCAL_DATE);
    }

//    public static String toString(Instant time) {
//        return temporalToString(time, YMDHMS, DateTimeFormatter.ISO_INSTANT);
//    }
//
//    public static String toString(Instant time, String pattern) {
//        if(StringUtils.isBlank(pattern)) pattern = YMDHMS;
//        return temporalToString(time, pattern, DateTimeFormatter.ISO_INSTANT);
//    }

    public static void main(String[] args) {
        System.out.println(toString(LocalDateTime.now()));
    }

    /**
     * 时间格式转字符串形式 yyyy-MM-dd HH:mm:ss
     * @param date 时间
     * @return
     */
    public static String toString(Date date){
        return temporalToString(Instant.ofEpochMilli(date.getTime()), YMDHMS, DateTimeFormatter.ISO_INSTANT);
    }

    /**
     * 时间格式转字符串形式
     * @param date  时间
     * @param pattern 格式
     * @return
     */
    public static String toString(Date date, String pattern){
        if(StringUtils.isBlank(pattern)) pattern = YMDHMS;
        return temporalToString(Instant.ofEpochMilli(date.getTime()), pattern, DateTimeFormatter.ISO_INSTANT);
    }

    private static String temporalToString(TemporalAccessor temporal, String pattern, @NotNull DateTimeFormatter defaultFormatter) {
        if(temporal == null) return null;
        try {
            DateTimeFormatter sf;
            if (StringUtils.isNotBlank(pattern)) sf = DateTimeFormatter.ofPattern(pattern);
            else sf = defaultFormatter;

            return sf.format(temporal);
        } catch (Exception e) {
            log.error("日期格式不匹配, pattern={}, err={}", pattern, e.getMessage());
        }
        return null;
    }

    private static TemporalAccessor stringToTemporal(String str, String pattern, @NotNull DateTimeFormatter defaultFormatter) {
        if(null != str) str = str.trim();
        if(StringUtils.isBlank(str)) return null;

        try {
            DateTimeFormatter sf;
            String dateStr = str;
            if(StringUtils.isNotBlank(pattern)) {
                if (pattern.startsWith(YMD) && !(pattern.contains("/"))) dateStr = dateStr.replace("/", "-");
                if (dateStr.length() <= 10) {
                    if (pattern.startsWith(YMD)) pattern = YMD;
                    else if (pattern.startsWith(YMD2)) pattern = YMD2;
                }
                if(pattern.equals(YMDHMS)){
                    dateStr = dateStr.replace("T", " ");
                    if(dateStr.contains(".")) dateStr = dateStr.substring(0, dateStr.indexOf("."));
                }
                sf = DateTimeFormatter.ofPattern(pattern);
            } else {
                sf = parseDateFormat(dateStr);
            }
            if(null != sf) return sf.parse(dateStr);
        } catch (Exception e) {
//            log.warn("日期格式不匹配,将使用标准格式转换, str={}, pattern={}, err={}", str, pattern, e.getMessage());
        }
        try {
            return defaultFormatter.parse(str);
        } catch(Exception e) {
            log.error("日期格式不匹配, str={}, pattern={}, err={}", str, pattern, e.getMessage());
        }
        return null;
    }

    public static String formatCurrentYearMonthDay() {
        return format(YMD2);
    }

    public static String format(String format) {
        LocalDateTime localDateTime = LocalDateTime.now();
        return localDateTime.format(DateTimeFormatter.ofPattern(format));
    }

}
